import numpy
import image_lib
import sys
#import pylab



size_min_y=32
size_min_x=2
max_height=1000



def l2_norm(i):
    """ calculate l2 norm of i """
    d=sum(i**2)
    while numpy.size(d) > 1:
        d=sum(d)
    return d 

def make_quadratic(i, qlen):
    """ transforms an image/rectangle to quadrat"""
    xlen=i.shape[1]
    ylen=i.shape[0]
    if (ylen > qlen):
        ylen=qlen
    if (xlen > qlen):
        xlen=qlen
    #print i.shape
    i2=numpy.zeros([qlen, qlen])
    for x in xrange(0,xlen):
        for y in xrange(0, ylen):
            i2[y,x]=i[y,x]
    return i2        

def empty_border(i):
    """ set border of image to zero """
    xlen=i.shape[1]
    ylen=i.shape[0]
    i[0:ylen, 0]=0
    i[0:ylen, xlen-1]=0
    i[0, 0:xlen]=0
    i[ylen-1, 0:xlen]=0
    return i
             
def locate_cell(i):
    #    flood_y=0
    #    x_len=i.shape[1]
    #    y_len=i.shape[0]
    #    y_max=int(y_len/4)
    #    ok=1
    #    y=0
    #    while ok:
    #        if i[y,x_len//2]>=1:
    #            ok=2
    #            flood_y=y+1
    #        
    #        if (ok==2):
    #            if not(i[y,x_len//2]>=1):
    #                ok=0
    #        if (y > y_len//2):
    #            ok=0
    #        y+=1
    #    i2=image_lib.flood_fill(i, x_len//2, flood_y, 0, 0.5)
    #    return i2
    #i*=0.5
    i=(1.0-i)
    i2=image_lib.segment_picture(i, 1.0)
    #bestimme objekt mit groesster flaeche- meisten pixel
    hi=numpy.histogram(i2, i2.max()+1)
    color_index=hi[0].argmax()
    #print hi
    i3=(i2==color_index)
    i3=i3.astype(float)
    #pylab.figure()
    #pylab.matshow(i3)
    return i3

def extract_number(i):
    min_width=4
    min_height=10
    i2=image_lib.segment_picture(i, 1.0)

    hi=image_lib.histogramm(i2)
    maxh=0
    idx=0
    #pylab.figure()
    #pylab.plot(hi)
    #pylab.matshow(i2)
    i4=None
    for n in xrange(1, len(hi)):
        if n > 0:
            i3=(i2==n)
            i3=i3.astype(float)
            #i3/=float(i3.max())
            #pylab.matshow(i3)
            minx,miny,maxx,maxy=image_lib.get_koordinates_roi(i3)

            w=abs(maxx-minx)
            d=abs(maxy-miny)
            #object is not connected to the border
            if (minx >0) and (maxx < i2.shape[1]-1):
                if (miny > 0) and (maxy < i2.shape[0]-1):
                    if w > min_width:
                        if d > min_height:
                            i4=i3.copy()
                            return i4
                    
    #print hi


    #pylab.matshow(i3)
    return i4
   

def compensate_rotation(i):
    i2=image_lib.reduce_size_roi(i)
    #print i2
    #pylab.figure()
    #pylab.imshow(i2)
    #print i
    x_len=i2.shape[1]
    h1=0
    x=int(x_len/3.0)
    while i2[h1, x]>=1.0:
        h1+=1
    x=int(x_len*2/3)
    h2=0
    while i2[h2, x]>=1.0:
        h2+=1
    h=h2-h1
    alpha=numpy.arctan(h/(x_len/3.0))
    #print alpha, h1, h2
    i3=numpy.misc.imrotate(i2, alpha)
    i3=(i3>0)
    i3=i3.astype(float)
    return i3
    
def close_image(i):
    i2=i.copy()
    s=i.shape
    for y in xrange(1, s[0]-1):
        for x in xrange(1, s[1]-1):

            ns=sum(sum(i[y-1:y+1,x-1:x+1]))
            
            if ns >= 3:
                i2[y,x]=1.0
            else:
                i2[y,x]=0.0
    return i2
                
def border_fill(i):
    x_len=i.shape[1]
    y_len=i.shape[0]
    for y in xrange(0, y_len):
        i[y,0:3]=1.0
        i[y, x_len-4:x_len]=1.0
    for x in xrange(0, x_len):
        i[0:3, x]=1.0
        i[y_len-4:y_len, x]=1.0
    return i

def calc_row_sum(i):
    y_len=i.shape[0]
    i2=numpy.zeros(y_len)
    for y in xrange(0, y_len):
        i2[y]=sum(i[y,:])
    return i2

def invert_image(i):
    i2=(1-i.copy())
    return i2

def init_number_dict(): 
    #pylab.ion()
    #i=image_lib.jaehne_test_pattern(400)
    #i=numpy.misc.imread("img_6373.jpg")
    i=image_lib.load("numbers.jpg")
    #print i
    #i=numpy.lena().copy()
    #i=numpy.misc.imresize(i, 0.2)
    i=image_lib.convert_to_grayscale(i)
    
    #pylab.gray()
    #hi=numpy.histogram(i, 256)
    #fb_index=numpy.argmax(hi[0])
    #fb_value=hi[1][fb_index]
    #i2=image_lib.binarize(i, fb_value)
    fb_value=i.min()+(i.max()-i.min())/2.0
    i2=(i<fb_value)
    i2=i2.astype(float)
    #pylab.imshow(i)
    i3=i2#i3=image_lib.flood_fill(i2, 0, 0, 0, 0.5)
    i4=image_lib.segment_picture(i3, 1)
    #pylab.figure()
    #print hi
    #pylab.plot(hi[0])
    number_dict=dict()
    #pylab.figure()
    #pylab.imshow(i4)
    maxv=int(i4.max())
    #pylab.figure()
    for n in xrange(1, maxv+1):
        i5=(i4==n)
        i5=i5.astype(float)
        minx,miny,maxx, maxy=image_lib.get_koordinates_roi(i5)
        i6=i5[miny-1:maxy+2, minx-1:maxx+2].copy()
        #print minx,miny,maxx,maxy
        if (i6.shape[0] >2) and (i6.shape[1] > 2) :
    
            i6=image_lib.flood_fill(i6, 0,0, 0, 0.5)
            i6=(i6==0.5)
            i6=i6.astype(float)
            i6=(1-i6)
            #pylab.matshow(i6)
            i6=image_lib.reduce_size_roi(i6)
            scale_fac=32/float(i6.shape[0])
            i6=image_lib.resize(i6, [int(scale_fac*i6.shape[1]), 32])
    
            #pylab.subplot(1, maxv, n)
            #i7=image_lib.calc_row_sum_horizontal(i6)
            i7=make_quadratic(i6.copy(), 32)
            #i7=calc_row_sum(i7)
            i7/=float(i7.max())
            #print i7.shape
            number_dict[n-1]=i7.copy()#image_lib.get_entropy(i7)
            #print number_dict[n-1]
            #pylab.plot(i7)
            #e=image_lib.get_histo_entropy(i6)
            #print "entropy ", e, " ", n
    #print number_dict
    #print "fertig!"
    #pylab.show()
    return number_dict
    
def get_sudoku_from_img(parent=None, filename=""):    
    #pylab.ion()
    if filename == "":
        s_mat=numpy.zeros([9,9])
    else:
        if parent!=None:
            parent.generate_progress_dialog(100)
        number_dict=init_number_dict()
        #print "init"  
        #i=image_lib.jaehne_test_pattern(400)
        #i=numpy.misc.imread("img_6373.jpg")
        i=image_lib.load(filename)
        if i.shape[0] > max_height:
            scale=(int(max_height/i.shape[0]*i.shape[1]), max_height)
            #print scale
            i=image_lib.resize(i, scale)
            #pylab.ion()
            #pylab.imshow(i)
            #print i
        #i=numpy.lena().copy()
        #i=numpy.misc.imresize(i, 0.2)
        i=image_lib.convert_to_grayscale(i)
        #print "grayscale"
        i=image_lib.white_balance(i)
        #print "whitebalance"
        #grenzwert fuer vorder- hintergrund bestimmen
        fb_value=i.min()+(i.max()-i.min())/2.0
        #voder hintergrund trennen
        i2=(i<fb_value)
        i2=i2.astype(float)
        #hi2=numpy.histogram(i2, 256)
        i2=empty_border(i2)
        #print "empty border"
        #i2=close_image(i2)
        #print "close"
        #print i2.shape
        i2=image_lib.reduce_size_roi(i2)
        #print i2.shape
        #pylab.figure()
        #pylab.matshow(i2)
        #i2=invert_image(i2)
        #i2=compensate_rotation(i2)
        ##pylab.figure()
        #
        #i2=invert_image(i2)
        #i2=close_image(i2)
        
        #print i2.shape
        #i2=image_lib.reduce_size_roi(i2)
        x_len=i2.shape[1]/9.0
        y_len=i2.shape[0]/9.0
        #pylab.ion()
        #print i2.shape
        #pylab.gray()
        #pylab.matshow(i2)
        #gitter raus
        pc=0
        s_mat=numpy.zeros([9,9])
        for y in xrange(0, 9):
            for x in xrange(0,9):
                i3=i2[y*y_len:(y+1)*y_len, x*x_len:(x+1)*x_len].copy()
                #pylab.figure()
                #pylab.imshow(i3)
                if parent!=None:
                    parent.show_progress(pc)
                    if pc < 100:
                        pc+=1
                    else:
                        pc=0
                i4=locate_cell(i3)
                i5=(i4==1.0)
                i5=i5.astype(float)
                i5=image_lib.reduce_size_roi(i5)
                i5=invert_image(i5)
                #pylab.matshow(i5)
                i5=extract_number(i5)
                s_mat[y,x]=0
                if i5 != None:
                    #print i5.shape
                    #pylab.matshow(i5)
                    #print xmin
                    i5=image_lib.reduce_size_roi(i5)
                    if (i5.shape[0]>size_min_y) and (i5.shape[1]>size_min_x):
                        
                        scale_fac=32/float(i5.shape[0])
                        i6=image_lib.resize(i5, [int(scale_fac*i5.shape[1]), 32])
                        #i6=numpy.ones([32,32])
                        #pylab.figure()
                        #pylab.matshow(i6)
                        #i7=image_lib.calc_row_sum_horizontal(i6)
                        i7=make_quadratic(i6.copy(), 32)
                        #i7=calc_row_sum(i7)
                        #i7=numpy.ones([32,32])
                        i7/=float(i7.max())
                        #print i7.shape
                        #m=image_lib.get_entropy(i7)
                        d=32*32
                        number=0
                        #print i7
                        for n in number_dict.keys():
                            #print i7.shape, number_dict[n].shape
                            dn=l2_norm((i7-number_dict[n]))
                            #print dn
                            #dn=d
            
                            if dn < d:
                                number=n
                                d=dn
                        s_mat[y,x]=number
                        #pylab.figure()
                        #pylab.plot(i7)
                        #print "entropy ", image_lib.get_entropy(i7)
        
        if parent!=None:
            parent.close_progress_dialog()
        #print flood_x
        #pylab.show()
    return s_mat


  
if __name__=="__main__":
    if len(sys.argv) > 1:
        print sys.argv[1]
        mat=get_sudoku_from_img(None, sys.argv[1])
        print mat 
    else:
        #pylab.gray()
        mat=get_sudoku_from_img(None, "2012-07-02sudoku_sz.jpg")
        print mat 
        
        mat=get_sudoku_from_img(None, "sudoku1.jpg")
        print mat 
        mat=get_sudoku_from_img(None, "sudoku2.jpg")
        print mat 
        mat=get_sudoku_from_img(None, "sudoku3.jpg")
        print mat 
        mat=get_sudoku_from_img(None, "sudoku4.jpg")
        print mat 
        #pylab.show()
